package app

import (
	"context"
	"net/http"
	"strconv"

	"gitee.com/huangxinbo/xcas/internal/app/base"
	"gitee.com/huangxinbo/xcas/internal/config"
	"gitee.com/huangxinbo/xcas/internal/controller"
	"gitee.com/huangxinbo/xcas/internal/dao"
	"gitee.com/huangxinbo/xcas/internal/middleware"
	"gitee.com/huangxinbo/xcas/internal/service"
	"gitee.com/huangxinbo/xcas/pkg"
	"gitee.com/huangxinbo/xcas/pkg/asynq_instance"
	"gitee.com/huangxinbo/xcas/pkg/db"
	"gitee.com/huangxinbo/xcas/pkg/i18n"
	"gitee.com/huangxinbo/xcas/pkg/logger"
	"gitee.com/huangxinbo/xcas/pkg/redis_client"
	"github.com/gin-contrib/location"
	"github.com/gin-gonic/gin"
	"github.com/hibiken/asynq"
	"github.com/redis/go-redis/v9"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	"gorm.io/gorm"
)

type App struct {
	Config      *config.Config
	DbInstance  *gorm.DB
	Redis       *redis.Client
	I18n        *i18n.I18n
	Logger      *logger.Logger
	Router      *gin.Engine
	Server      *http.Server
	AsynqClient *asynq.Client

	DAO        *dao.DAO
	Service    *service.Service
	Controller *controller.Controller
}

func NewApp() (*App, error) {
	app := &App{}

	// Initialize configuration
	cfg := config.InitConfig()
	app.Config = cfg

	// Initialize logger
	log, err := logger.NewLogger("app", *cfg)
	if err != nil {
		return nil, err
	}
	app.Logger = log

	// Initialize database
	database, err := db.NewDB(cfg, log)
	if err != nil {
		log.Error("DB connection failed, error: " + err.Error())
		return nil, err
	}
	app.DbInstance = database

	// Initialize Redis
	redisClient, err := redis_client.NewRedis(cfg, log)
	if err != nil {
		log.Error("Redis connection failed, error: " + err.Error())
		return nil, err
	} else {
		app.Redis = redisClient
	}

	// Initialize i18n
	i18n := i18n.NewI18n(pkg.GetEnvString("I18N.LOCALES.PATH", "locales") + "/app")
	app.I18n = i18n

	app.AsynqClient = asynq_instance.NewAsnyqClient(cfg.Redis, cfg.Asynq)

	base := base.NewBase(cfg, log, i18n, database, redisClient, nil, app.AsynqClient, nil)

	// Initialize DAO
	app.DAO = dao.NewDAO(base)

	// Initialize Service
	app.Service = service.NewService(base, app.DAO)

	// Initialize Controller
	app.Controller = controller.NewController(base, app.Service)

	// Initialize router
	app.Router = InitRouter(app, base)

	// Initialize HTTP server
	app.Server = &http.Server{
		Addr:    ":" + strconv.Itoa(app.Config.Server.Port),
		Handler: app.Router,
	}

	return app, nil
}

func InitRouter(app *App, base *base.Base) *gin.Engine {
	gin.SetMode(pkg.GetEnvString("SERVER.MODE", gin.ReleaseMode))
	router := gin.New()

	// Register middleware
	m := &middleware.Middleware{
		Logger: app.Logger,
		I18n:   app.I18n,
	}
	router.Use(
		location.Default(),
		m.RequestIdMiddleware(logger.XRequestId),
		m.ErrorMiddleware(),
		m.I18nMiddleware(),
		m.GinLoggerMiddleware(),
		m.GinRecoveryMiddleware(true),
		m.CORSMiddleware(),
	)
	app.Logger = m.Logger

	// Register Swagger routes
	router.GET("/swagger/*any", ginSwagger.WrapHandler(
		swaggerFiles.Handler,
		ginSwagger.DefaultModelsExpandDepth(-1),
	))

	// Handle direct /cas route (without trailing slash)
	router.GET("/cas", func(c *gin.Context) {
		c.File("./resources/ui/index.html")
	})

	// Serve static files with custom handler to avoid redirects
	router.GET("/cas/*filepath", func(c *gin.Context) {
		filepath := c.Param("filepath")
		if filepath == "/" || filepath == "" {
			c.File("./resources/ui/index.html")
		} else {
			// Check if the file exists
			fullPath := "./resources/ui" + filepath

			// List of known static file extensions
			staticExtensions := []string{".js", ".css", ".svg", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".woff", ".woff2", ".ttf", ".eot"}

			// Check if this is a static file request
			isStaticFile := false
			for _, ext := range staticExtensions {
				if len(filepath) > len(ext) && filepath[len(filepath)-len(ext):] == ext {
					isStaticFile = true
					break
				}
			}

			if isStaticFile {
				// Serve static files directly
				c.File(fullPath)
			} else {
				// For SPA routes, serve index.html
				c.File("./resources/ui/index.html")
			}
		}
	})

	// 添加认证中间件
	router.Use(m.AuthMiddleware(base))

	// Register routes
	app.Controller.AppController.RegisterRoutes(router)
	app.Controller.CasController.RegisterRoutes(router)
	app.Controller.CaptchaController.RegisterRoutes(router)
	app.Controller.UserController.RegisterRoutes(router)
	app.Controller.FileController.RegisterRoutes(router)
	app.Controller.OAuthController.RegisterRoutes(router)

	return router
}

func (app *App) Run() error {
	app.Logger.Info("Starting server on " + app.Server.Addr)
	return app.Server.ListenAndServe()
}

func (app *App) Shutdown(ctx context.Context) error {
	// Gracefully shutdown HTTP server
	if err := app.Server.Shutdown(ctx); err != nil {
		return err
	}

	// Close other resources
	return app.Close()
}

func (app *App) Close() error {
	if app.AsynqClient != nil {
		app.AsynqClient.Close()
	}
	// Close database connections and other resources
	if app.DbInstance != nil {
		sqlDB, _ := app.DbInstance.DB()
		return sqlDB.Close()
	}
	if app.Redis != nil {
		return app.Redis.Close()
	}
	if app.Logger != nil {
		return app.Logger.Logger.Sync()
	}
	return nil
}
